package com.aikon.wht.reflect;

import com.google.common.collect.Lists;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;

/**
 * @author haitao.wang
 */
public class TestOfReflect implements Interface1,Interface2{

    static int cnt = 21; // start line of ss*.

    private static void out(Class c, ReflectModel b, Field f1, Field f2, Field f3, Field f4, Field f5, Field f6, Method m1) throws NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
        ss(f1.getType());
        ss(f1.getGenericType());
        ss(f1.getClass());
        ss(f1.isAccessible());
        ss(f2.getName());
        ss(c.getDeclaredField("f2"));
        ss(b);
        ss(f2.get(b));
        ss(f1.get(b));
        ss(f1.getAnnotatedType());
        ss(f1.getAnnotation(Annotation1.class));
        ss(f1.getAnnotationsByType(Annotation1.class)[0].ann1());
        ss(f1.getDeclaredAnnotations()[0]);
        ss(f1.getType() == List.class);
        ss(f2.getType() == String.class);
        ss(f1.getGenericType().getClass());
        ss(f1.getGenericType().getTypeName());
        ss(f1.getGenericType() instanceof ParameterizedType);
        ss(f2.getGenericType().getTypeName());
        ss(f1.getModifiers());
        ss(f2.getModifiers());
        ss(Modifier.isPrivate(f1.getModifiers()));
        ss(Modifier.isPrivate(f2.getModifiers()));
        ss(f1.isEnumConstant());
        ss(f1.isSynthetic());
        ss(f1.toString());
        ss(f1.toGenericString());
        ss(f3.isEnumConstant());
        ss(ReflectModel.FieldEnum.class.getDeclaredField("e1").isEnumConstant());
        ss(f3.getType() == Enum.class);
        ss(Modifier.isStatic(f3.getModifiers()));
        ss(f4.getGenericType() instanceof TypeVariable);
        ss(((TypeVariable) f4.getGenericType()).getBounds()[0]);
        ss(((TypeVariable) f4.getGenericType()).getAnnotatedBounds());
        ss(((TypeVariable) f4.getGenericType()).getGenericDeclaration());
        ss(((TypeVariable) f4.getGenericType()).getName());
        ss(((TypeVariable) f4.getGenericType()).getClass());
        ss(((TypeVariable) f4.getGenericType()).getDeclaredAnnotations());
        ss(((ParameterizedType) f5.getGenericType()).getActualTypeArguments()[0].getTypeName());
        ss(((ParameterizedType) f5.getGenericType()).getOwnerType());
        ss(((ParameterizedType) f5.getGenericType()).getRawType());
        ss(((ParameterizedType) f5.getGenericType()).getTypeName());
        ss(((ParameterizedType) f1.getGenericType()).getActualTypeArguments()[0] instanceof WildcardType);
        ss(((WildcardType) ((ParameterizedType) f1.getGenericType()).getActualTypeArguments()[0]).getUpperBounds()[0]);
        ss(((WildcardType) ((ParameterizedType) f1.getGenericType()).getActualTypeArguments()[0]).getLowerBounds());
        ss(((WildcardType) ((ParameterizedType) f1.getGenericType()).getActualTypeArguments()[0]).getTypeName());
        ss(((WildcardType) ((ParameterizedType) f1.getGenericType()).getActualTypeArguments()[0]));
        ss(f6.getGenericType() instanceof GenericArrayType);
        ss(((GenericArrayType) f6.getGenericType()).getGenericComponentType());
        ss(((GenericArrayType) f6.getGenericType()).getTypeName());
        ss(m1.getParameterAnnotations()[0][0]);
        ss(m1.getParameterAnnotations()[0][1]);
        ss(m1.getParameterAnnotations()[1][0]);
        ss(m1.getDeclaringClass());
        ss(m1.getReturnType());
        ss(m1.isBridge());
        ss(TestOfReflect.class.newInstance().kick());
        ss(Method.class.getSuperclass());
        ss(c.toString());
        ss(c.toGenericString());
        ss(c.isInterface());
        ss(FunctionalInterface.class.isInterface());
        ss(FunctionalInterface.class.isAnnotation());
        ss(Function.class.isInterface());
        ss(Method.class.isAssignableFrom(Executable.class));
        ss(AnnotatedElement.class.isAssignableFrom(Method.class));
        ss(AnnotatedElement.class.isInstance(m1));
        ss(Executable.class.isInstance(m1));
        ss(new String[]{}.getClass().isArray());
        ss(Method.class.isArray());
        ss(Method.class.isPrimitive());
        ss(Boolean.class.isPrimitive());
        ss(boolean.class.isPrimitive());
        ss(boolean.class.isAssignableFrom(Boolean.class));
        ss(boolean.class.isInstance(Boolean.TRUE));
        ss(Boolean.class.isAssignableFrom(boolean.class));
        ss(Boolean.TYPE.isPrimitive());
        ss(Boolean.TYPE == boolean.class);
        ss(Long.TYPE == long.class);
        ss(f1.getType().getName());
        ss(f1.getType().getClassLoader());
        ss(b.getClass().getClassLoader());
        ss(c.getTypeParameters()[0]);
        ss(((TypeVariable)c.getTypeParameters()[0]).isAnnotationPresent(Annotation1.class));
        ss(f1.isAnnotationPresent(Annotation1.class));
        ss(Arrays.toString(m1.getTypeParameters()));
        ss(Arrays.toString(b.getClass().getTypeParameters()));
        ss((b.getClass().getTypeParameters()[0]));
        ss(Object.class.isInstance(null));
        ss(Object.class.isAssignableFrom(b.getClass()));
        ss(((TypeVariable)b.getClass().getTypeParameters()[0]).getBounds()[0]);
    }

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class c = ReflectModel.class;
        ReflectModel b = (ReflectModel) c.newInstance();
        b = new ReflectModel<Long,String>();
        Field f2 = c.getDeclaredField("f2");
        Field f1 = c.getDeclaredField("f1");
        Field f3 = c.getDeclaredField("f3");
        Field f4 = c.getDeclaredField("f4");
        Field f5 = c.getDeclaredField("f5");
        Field f6 = c.getDeclaredField("f6");
        Method[] ms = c.getDeclaredMethods();
        Method m1 = c.getDeclaredMethod("method1", Collection.class, Object.class);
        m1.invoke(b,new ArrayList<Long>(), "ss");


        ReflectModel<?, ?>[] rs = new ReflectModel[2];

//        f2.setAccessible(true);
        AccessibleObject.setAccessible(new AccessibleObject[]{f1, f2, f3, f4, f5, f6, m1}, true);

        f1.set(b, Lists.newArrayList("sdfasdf"));
        f2.set(b, "asdfasdf");

        out(c, b, f1, f2, f3, f4, f5, f6, m1);

    }

    public static void ss(Object object) {
        System.out.println(cnt + " " + object);
        cnt++;
    }

    /**
     * Implements two interfaces.
     *
     * @return String
     */
    @Override
    public String kick() {

        return "kick";
    }
}
